from django.contrib.auth import login,logout
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views import View
from django.http import HttpResponseForbidden,HttpResponse,JsonResponse
import re
from pymysql import DatabaseError

from apps.carts.utils import merge_cart_cookie_to_redis
from apps.goods.models import SKU
from apps.registers.models import User,Address
from meiduo_mall.settings.dev import logger
from utils.response_code import RETCODE
import django_redis
import json
from celery_tasks.emails.tasks import Send_Email
from apps.oauth.utils import Serretoauth
from django.conf import settings
# 保存和展示浏览记录
class History(LoginRequiredMixin,View):
    """
    统计浏览记录，通过redis数据库列表记录客户访问的sku id
    通过sku id获取商品信息，为避免重复记录，判断下sku_id是否存在
    存在的话就先清空数据库的相同sku_id再记录。
    """
    def post(self,request):
        sku_id = json.loads(request.body.decode()).get("sku_id")
        redis_ob = django_redis.get_redis_connection('visit')
        p1 = redis_ob.pipeline()
        # 存储数据(查看整个收据库中的sku_id)
        try:
            p1.lrem(request.user.id,0,sku_id)
            p1.lpush(request.user.id,sku_id)
        except Exception as f:
            logger.error(f)
            return HttpResponseForbidden("sku_id不存在")
        p1.execute()
        return JsonResponse({"code":RETCODE.OK,"ermsg":"OK"})
    """
    显示浏览记录，链接数据库，获取到所有的sku_id
    遍历后根据sku_id获取sku对象，查表返回给前端需要数据格式
    """
    def get(self,request):
        redis_ob = django_redis.get_redis_connection("visit")
        skus = redis_ob.lrange(request.user.id,0,-1)
        #遍历skus
        visit_list = []
        for sku in skus:
            sku = SKU.objects.get(id=sku.decode())
            visit_list.append({
                "id":sku.id,
                "name":sku.name,
                "default_image_url":sku.default_image.url,
                "price":sku.price
            })


        return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'skus': visit_list})
# 修改密码
class PasswordView(LoginRequiredMixin,View):
    # 显示修改密码页面
    def get(self,request):
        return render(request, "user_center_pass.html")
    """
    1.接收传入的参数
    2.判空判正则
    3.使用django自带的校验密码的方法，因为从数据库中取出的数据是加密的无法直接比较
    4.更新密码
    5.重置到登录页面并且删除session和cookie
    """
    def post(self,request):
        # 1.接收参数
        old_password = request.POST.get("old_pwd")
        new_password = request.POST.get("new_pwd")
        checkN_password = request.POST.get("new_cpwd")
        # 2.判空判正则
        pass
        # 3.校验密码
        try:
            request.user.check_password(old_password)
        except Exception :
            return HttpResponseForbidden("原始密码不正确")
            # return render(request, 'user_center_pass.html', {'origin_pwd_errmsg':'原始密码错误'})
        # 如果两次输入的新密码一致更新密码
        if new_password != checkN_password :
            return render(request, 'user_center_pass.html', {'origin_pwd_errmsg': '两次密码设置不一致'})
        request.user.set_password(raw_password = new_password)
        request.user.save()
        logout(request)
        response = redirect(reverse("login"))
        response.delete_cookie("username")
        return response
# 实现修改标签
class TitleaddressView(LoginRequiredMixin,View):
    """
    1.获取变更对象
    2.变更
    """
    def put(self,requset,address_id):
        json_ob = json.loads(requset.body.decode())
        try :
            address_od = Address.objects.get(id = address_id)
            address_od.title =json_ob["title"]
            address_od.save()
        except Exception as f:
            logger.error(f)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '修改标题失败'})
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '修改标题成功'})
# 实现默认地址的变更
class DefaultaddressView(LoginRequiredMixin,View):
    """
    1.获取变更对象
    2.变更
    """
    def put(self,request,address_id):
        try :
            user_ob = User.objects.get(username=request.user)
            user_ob.default_address = Address.objects.get(id = address_id)
            user_ob.save()
        except Exception as f:
            logger.error(f)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '修改默认地址失败'})
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '修改默认地址成功'})
# 实现修改地址(删除)
class UpdateaddressView(LoginRequiredMixin,View):
    def put(self,request,address_id):
        """
        1.根据使用put传入的数据进行接受
        2.判空判正则
        3.修改数据
        4.返回给前端
        """
        # 1.接收参数
        json_ob = json.loads(request.body.decode())
        receiver = json_ob.get("receiver")
        province_id = json_ob.get("province_id")
        city_id = json_ob.get("city_id")
        district_id = json_ob.get("district_id")
        place = json_ob.get("place")
        mobile = json_ob.get("mobile")
        tel = json_ob.get("tel")
        email = json_ob.get("email")
        # 2.判断
        pass
        # 3.修改数据
        try:
            address_ob = Address.objects.get(id = address_id)
            address_ob.receiver = receiver
            address_ob.province_id = province_id
            address_ob.city_id = city_id
            address_ob.district_id = district_id
            address_ob.place = place
            address_ob.mobile = mobile
            address_ob.tel = tel
            address_ob.email = email
            address_ob.save()
        except Exception as f:
            logger.error(f)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '更新地址失败'})
        # 修改完成后再拼接响应对象时一定要再调用一下Address对象
        address_ob = Address.objects.get(id=address_id)
        address_list = {
            "id": address_ob.id,
            "title": address_ob.receiver,
            "receiver": address_ob.receiver,
            "province": address_ob.province.name,
            "city": address_ob.city.name,
            "district": address_ob.district.name,
            "place": address_ob.place,
            "mobile": address_ob.mobile,
            "tel": address_ob.tel,
            "email": address_ob.email}
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '更新地址成功','address': address_list})
    # 实现删除地址（逻辑/物理删除)
    def delete(self,request,address_id):
        """
        1.通过address_id确认要删除的地址
        2.跟新is_delete字段为True
        """
        # 1.物理删除
        try:
            Address.objects.get(id=address_id).delete()
        # try:
        #     address_ob = Address.objects.get(id=address_id)
        #     address_ob.is_deleted =True
        #     address_ob.save()
        except Exception as f:
            logger.error(f)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '删除地址失败'})

        return JsonResponse({'code': RETCODE.OK, 'errmsg': '删除地址成功'})
# 实现收获地址的添加
class AddaddressView(LoginRequiredMixin,View):
    def post(self,request):
        """
        新增的收获地址使通过表单的形式发送进来的
         1.接受参数
         2.校验参数（判空，判正则）————>无需判断是否相等
            receiver 收货人
            province_id 省id
            city_id 市id
            district_id 县id
            place 收货地址
            mobile  手机号
            tel 固话
            email   邮箱
         3.将数据保存到数据库
         4.提取数据返回给前端
         5.拼接json格式
        """
        # 1.接受参数,请求方式使json类型的
        # 1.1优化功能，判断地址总数是否超过20,超过则提示不让再次添加
        user_ob = User.objects.get(username=request.user)
        if user_ob.addresses.count() > 20 :
            print(user_ob.addresses.count())
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '新增地址失败'})
        json_ob = json.loads(request.body.decode())
        receiver = json_ob.get("receiver")
        province_id = json_ob.get("province_id")
        city_id = json_ob.get("city_id")
        district_id = json_ob.get("district_id")
        place = json_ob.get("place")
        mobile = json_ob.get("mobile")
        tel = json_ob.get("tel")
        email = json_ob.get("email")
        # 2.判断
        pass
        # 3.存储数据库

        try:
            address_ob = Address.objects.create(user=request.user,title=receiver,receiver=receiver,
            province_id=province_id,city_id=city_id,district_id=district_id,place=place,mobile=mobile,tel=tel,email=email)
            if not request.user.default_address:
                request.user.default_address = address_ob
                request.user.save()
        except Exception as f:
            logger.error(f)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '新增地址失败'})
        address_list = {
        "id" : address_ob.id,
        "title" : address_ob.receiver,
        "receiver" : address_ob.receiver,
        "province" : address_ob.province.name,
        "city" : address_ob.city.name,
        "district" : address_ob.district.name,
        "place" : address_ob.place,
        "mobile" : address_ob.mobile,
        "tel" : address_ob.tel,
        "email" : address_ob.email}
        # 3.1优化功能如果是唯一地址就把它设置为默认地址

        return JsonResponse({'code': RETCODE.OK, 'errmsg': '新增地址成功','address':address_list})
# 展示收获地址页面
class AddressView(LoginRequiredMixin,View):
    def get(self,request):
        address_list = []
        """
        遍历出所有没被逻辑删除的元素，然后将这些元素添加到列表中
        将这个列表用json格式发送给前端，前端会遍历后按列表显示
        """
        for address_ob in Address.objects.filter(user=request.user,is_deleted=False) :
            context1 = {
            "id": address_ob.id,
            "title": address_ob.title,
            "receiver": address_ob.receiver,
            "province": address_ob.province.name,
            "city": address_ob.city.name,
            "district": address_ob.district.name,
            "place": address_ob.place,
            "mobile": address_ob.mobile,
            "tel": address_ob.tel,
            "email": address_ob.email }
            address_list.append(context1)
        context = {
            'default_address_id': request.user.default_address,
            'addresses': address_list,
        }
        return render(request,"user_center_site.html",context)
# 实现邮箱的验证
class VerifyemailView(LoginRequiredMixin,View):
    """
    通过解密,获取验证的标识,通过验证标识达到绑定的目的,在数据库的
    email_active更新为ture,然后重定向到主页,注意继承下LoginRequireMixin判定下登录状态
    """
    def get(self,request):
        # 判断下token还存不存在
        try :
            token_ob = Serretoauth().Unsecret(request.GET.get("token"))
        except Exception as f:
            logger.error(f)
            return HttpResponseForbidden("token不存在")
        # 判断token中的判断标识和数据库中是否一致
        if User.objects.get(username=token_ob["username"],email=token_ob["email"]):
            # 一致的话把email_status字段更新为true,然后重定向到主页
            User.objects.filter(username=token_ob["username"]).update(email_status=True)
            return redirect(reverse("centents"))
        else :
            return HttpResponseForbidden("绑定失败,用户名邮箱异常")
 # 实现邮箱的绑定
# 实现绑定邮箱和发送验证码的逻辑
class EmailView(LoginRequiredMixin,View):
    """
    邮箱的字段我们有,这里是实现更新的方法.用put请求方式
    不是post请求,所以用body接受,格式使json,用json解码后是一个二进制在通过decode解码
    """
    def put(self,request):
        email_ob = json.loads(request.body.decode()).get("email")
        # 通过正则校验邮箱格式
        if re.match("r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$'",email_ob):
            return HttpResponseForbidden("邮箱格式错误")
        # 判断邮箱是否已经存在
        if User.objects.filter(email_status=True):
            return HttpResponseForbidden("邮箱已经绑定")
        try:
            request.user.email = email_ob
            request.user.save()
        except Exception as f:
            logger.error(f)
            return JsonResponse({"code":RETCODE.EMAILERR,"errmesg":"邮箱添加失败"})
        """拼接发送的链接内容,处理业务逻辑的接口,和需要验证绑定的信息,这些信息已经加密
        通过一个query字符传递,再另一个接口接受,然后验证即可"""
        email_content = Serretoauth().Secret({"username":request.user.username,"email":request.user.email})
        verify_url = settings.EMAIL_VERIFY_URL + "?token=" +email_content
        Send_Email.delay(email_ob,verify_url)
        return JsonResponse({"code":RETCODE.OK,"errmesg":"邮箱添加成功"})
# 实现展示用户中心页面
# 验证用户是否登录使用的是继承loginmixin这个类,但是当我们没登录时会重定向到错误的地址,
# 需要在setting文件中进行配置修改
class CenterView(LoginRequiredMixin,View):
    def get(self,request):
        context={
            "username" : request.user.username,
            "mobile" : request.user.mobile,
            "email" : request.user.email,
            "email_active": request.user.email_status
        }
        return render(request,"user_center_info.html",context)
# 实现退出界面
class LogoutView(View):
    def get(self,request):
        logout(request)
        response = redirect(reverse("login"))
        response.delete_cookie("username")
        return response
# 实现登录页面的显示
class LoginView(View):
    def get(self,request):
        return render(request,"login.html")
    """登录校验
    通过django自带的校验功能
    通过模型类authentucate创建对象
    """
    def post(self,request):
        username = request.POST.get("username")
        password = request.POST.get("pwd")
        remembered = request.POST.get("remembered")
        if not all([username,password]):
            return HttpResponseForbidden("请完整填写全部内容")
        if not re.match("^[a-zA-Z0-9_-]{5,20}$",username):
            return HttpResponseForbidden("用户名格式不正确")
        if not re.match("^[a-zA-Z0-9-]{8,20}$",password) :
            return HttpResponseForbidden("密码格式不正确")

        from django.contrib.auth import authenticate
        # 当全部相等时会返回一个对象
        user = authenticate(username=username,password=password)
        # 如果对象不存在,从新渲染登录界面,从新登录
        if user is None :
            # 这里需要注意掉坑context中的元素使字典类型key:value
            return render(request,"login.html",{'account_errmsg':'帐号密码错误'})
        # 保持登录
        login(request,user)
        if remembered != "on" :
            request.session.set_expiry(0)
        response = redirect(reverse("centents"))
        # 实现购物车的合并
        merge_cart_cookie_to_redis(request,user,response)

        response.set_cookie("username",request.user,max_age=24*60*60*14)
        # else:
        #     request.session.set_expiry(None)
        return response
# 验证手机号是否重复,思路和验证用户名一样,通过url拼接传参
class MobileView(View):
    def get(self,request,mobile):
        """
        筛选出输入的手机号和数据库中是否有一致的,如果有count=1
         没有的话count=0,把这个count返回给前端,前端进行比较
        """
        mobile_count=User.objects.filter(mobile=mobile).count()
        return JsonResponse({
            "code":RETCODE.OK,
            "eemsg":"ok",
            "count":mobile_count
        })
# 验证用户名是否重复
class UserCountView(View):
    def get(self,request,username):
        # 提取数据库中这个用户名的数量
        usercount = User.objects.filter(username=username).count()
        return JsonResponse({
            "code" :RETCODE.OK,
            "errmsg":"OK",
            "count":usercount
        })
# 注册界面
class RegisterView(View):
    def get(self,request):
        return render(request,"register.html")
    # 实现注册界面功能，需要一个mysql的数据库用来存注册的信息
    # 1.判断注册帐号表单不能为空，数据必须在5-20字符之间，且不能和数据库中数据重复
    # 2.判断注册密码表单不能为空，数据必须在8-20字符之间
    # 3.判断重复输入密码表单不能为空，数据必须和2一样
    # 6.判断是否同意美多协议
    def post(self,request):
        username = request.POST.get("user_name")
        password = request.POST.get("pwd")
        password2 = request.POST.get("cpwd")
        mobile = request.POST.get("phone")
        allow = request.POST.get("allow")
        sys_verification = request.POST.get('msg_code')
        # print(request.POST)
        # 判断是否为空/判断是否重复
        if not all([username,password,password2,mobile,allow]):
            return HttpResponseForbidden("表单不能为空")
        # 判断用户名
        if not re.match(r"^[0-9a-zA-Z]{5,20}$" ,username):
            return HttpResponseForbidden("格式不正确username")
        usercount = User.objects.filter(username=username).count()
        if usercount == 1 :
            return HttpResponseForbidden("用户名重复")
        if not re.match(r"^[0-9a-zA-Z]{8,20}$" , password):
            return HttpResponseForbidden("格式不正确password")
        if password2 != password:
            return HttpResponseForbidden("格式不正确password1")
        if not re.match(r"^1[3-9]{1}\d{9}$", mobile):
            return HttpResponseForbidden("格式不正确mobile")
        mobilescount = User.objects.filter(mobile=mobile).count()
        if mobilescount == 1:
            return HttpResponseForbidden("手机号已经存在")
        if allow != "on" :
            return HttpResponseForbidden("确认协议")
    # 4.判断验证码
    # 5.判断短信验证码
    #     redis_sys_ob = django_redis.get_redis_connection("sys_code")
    #     redis_ob = redis_sys_ob.get(mobile)
    #     if redis_ob == None :
    #         return render(request,"register.html",{"sms_code_errmsg":"验证码不存在"})
    #     if redis_ob.decode() != sys_verification :
    #         return render(request,"register.html",{"sms_code_errmsg":"验证码不正确"})


    # 6.保存数据,并却返回响应
        try :
            user = User.objects.create_user(username=username, password=password, mobile=mobile)
        except DatabaseError as f:
            return redirect(reverse("registers"))
        # 保持会话状态,参数user就是前面创建的注册对象，login要给这个对象设置session和cookie
        login(request,user)
        response = redirect(reverse("centents"))
        response.set_cookie("username", request.user, max_age=24 * 60 * 60 * 14)
        return response
